package agent

import (
	"context"
	"encoding/json"
	"errors"
	"gitee.com/gitee-go/core"
	"gitee.com/gitee-go/core/bean/hbtpBean"
	"gitee.com/gitee-go/core/common"
	"gitee.com/gitee-go/core/runtime"
	"gitee.com/gitee-go/utils"
	"gitee.com/gitee-go/utils/ioex"
	"net"
	"runtime/debug"
	"time"
)

/**
长连接实体
*/
type Client struct {
	//id string
	//name string
	info *hbtpBean.RegReq
	ctx  context.Context
	cncl context.CancelFunc
	head *runtime.ClientHeader
	conn net.Conn
	htm  time.Time

	sndch chan *runtime.ClientMsg
	rcvch chan *runtime.ClientMsg

	tmps utils.Map
}

func StartClient(m *hbtpBean.RegReq, ctx context.Context, conn net.Conn) *Client {
	c := &Client{
		info:  m,
		conn:  conn,
		sndch: make(chan *runtime.ClientMsg, 100),
		rcvch: make(chan *runtime.ClientMsg, 100),
		tmps:  utils.Map{},
	}
	c.ctx, c.cncl = context.WithCancel(ctx)
	c.start()
	return c
}
func (c *Client) start() {
	go func() {
		for !c.Stopd() {
			if err := c.runRead(); err != nil {
				core.Log.Errorf("Client runRead err(end):%+v", err)
				c.Stop()
			}
		}
		if c.conn != nil {
			c.conn.Close()
		}
	}()
	go func() {
		for !c.Stopd() {
			c.runWrite()
		}
	}()
	go func() {
		c.htm = time.Now()
		for !c.Stopd() {
			c.runCheck()
			time.Sleep(time.Millisecond)
		}
		//AgentEngine.clients.Delete(c.head.Id)
	}()
}

// 是否已结束
func (c *Client) Stopd() bool {
	return ioex.CheckContext(c.ctx)
}

// 获取心跳时间
func (c *Client) HeartTime() time.Time {
	return c.htm
}
func (c *Client) Stop() {
	if c.conn != nil {
		c.conn.Close()
	}
	if c.cncl != nil {
		c.cncl()
	}
	if c.sndch != nil {
		close(c.sndch)
		c.sndch = nil
	}
	if c.rcvch != nil {
		close(c.rcvch)
		c.rcvch = nil
	}
}
func (c *Client) Info() hbtpBean.RegReq {
	return *c.info
}

// 读取长连接数据并解析成数据包
func (c *Client) runRead() error {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("Client runRead:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
		}
	}()

	bts, err := ioex.TcpRead(c.ctx, c.conn, 1) //2个字节开头[0]:0x8d
	if err != nil {
		return err
	}
	if bts[0] != 0x8d {
		core.Log.Error("Client runRead 0x8d what????")
		return nil
	}
	bts, err = ioex.TcpRead(c.ctx, c.conn, 1) //2个字节开头[1]:0x8f
	if err != nil {
		return err
	}
	if bts[0] != 0x8f {
		core.Log.Error("Client runRead 0x8f what????")
		return nil
	}
	bts, err = ioex.TcpRead(c.ctx, c.conn, 4) //4字节path长度
	if err != nil {
		return err
	}
	pathln := uint(ioex.BigByteToInt(bts))
	bts, err = ioex.TcpRead(c.ctx, c.conn, 4) //4字节head长度
	if err != nil {
		return err
	}
	hdln := uint(ioex.BigByteToInt(bts))
	bts, err = ioex.TcpRead(c.ctx, c.conn, 4) //4字节body长度
	if err != nil {
		return err
	}
	bodyln := uint(ioex.BigByteToInt(bts))

	if pathln > common.MAX_CLI_PATH_LEN {
		core.Log.Errorf("Client runRead pathln out:%d/%d", pathln, common.MAX_CLI_PATH_LEN)
		return errors.New("pathln out max")
	}
	if hdln > common.MAX_CLI_HEAD_LEN {
		core.Log.Errorf("Client runRead hdln out:%d/%d", hdln, common.MAX_CLI_HEAD_LEN)
		return errors.New("hdln out max")
	}
	if bodyln > common.MAX_CLI_BODY_LEN {
		core.Log.Errorf("Client runRead bodyln out:%d/%d", bodyln, common.MAX_CLI_BODY_LEN)
		return errors.New("bodyln out max")
	}
	// 把数据流解析成一个个结构体
	msg := &runtime.ClientMsg{}
	if pathln > 0 {
		bts, err := ioex.TcpRead(c.ctx, c.conn, pathln)
		if err != nil {
			return err
		}
		msg.Path = string(bts)
	}
	if hdln > 0 {
		msg.Head = utils.Map{}
		bts, err := ioex.TcpRead(c.ctx, c.conn, hdln)
		if err != nil {
			return err
		}
		err = json.Unmarshal(bts, &msg.Head)
		if err != nil {
			return nil
		}
	}
	if bodyln > 0 {
		bts, err := ioex.TcpRead(c.ctx, c.conn, bodyln)
		if err != nil {
			return err
		}
		msg.Body = bts
	}
	bts, err = ioex.TcpRead(c.ctx, c.conn, 2)
	if err != nil {
		return err
	}
	if c.rcvch != nil && bts[0] == 0x8e && bts[1] == 0x8f {
		c.rcvch <- msg
		c.htm = time.Now()
	}
	return nil
}

// 把数据包从连接流发送出去
func (c *Client) runWrite() {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("Client runWrite:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
		}
	}()

	if c.sndch == nil {
		return
	}
	msg := <-c.sndch
	var hds []byte
	if msg == nil {
		core.Log.Errorf("Client runWrite json err")
		return
	}
	if msg.Head != nil {
		bts, err := json.Marshal(msg.Head)
		if err != nil {
			core.Log.Errorf("Client runWrite json err:%+v", err)
			return
		}
		hds = bts
	}
	// 吧结构体转成数据流
	paths := []byte(msg.Path)
	pathln := ioex.BigIntToByte(int64(len(paths)), 4)
	hdln := ioex.BigIntToByte(int64(len(hds)), 4)
	bodyln := ioex.BigIntToByte(int64(len(msg.Body)), 4)
	c.conn.Write([]byte{0x8d, 0x8f}) // 每个包的开头
	c.conn.Write(pathln)             //长度
	c.conn.Write(hdln)               //长度
	c.conn.Write(bodyln)             //长度
	if len(paths) > 0 {
		c.conn.Write(paths)
	}
	if len(hds) > 0 {
		c.conn.Write(hds)
	}
	if len(msg.Body) > 0 {
		c.conn.Write(msg.Body)
	}
	c.conn.Write([]byte{0x8e, 0x8f}) // 包结尾
}

// 检查超时,获取数据包调用相应处理函数
func (c *Client) runCheck() {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("Client runCheck:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
		}
	}()

	tms := time.Since(c.htm).Seconds()
	if tms > 12 { // 心跳5秒发一次,12秒内有两次
		c.Stop()
	}
	// 弃用
	select {
	case msg := <-c.rcvch:
		if fn, ok := mpCliFn[msg.Path]; ok && fn != nil {
			fn(c, msg)
		}
	default:
		time.Sleep(time.Millisecond)
	}
}

// 把数据包加入发送队列
func (c *Client) Send(path string, body []byte, hds ...utils.Map) {
	if path == "" {
		//return errors.New("param err")
		return
	}
	msg := &runtime.ClientMsg{Path: path, Body: body}
	if len(hds) > 0 {
		msg.Head = hds[0]
	}
	c.sndch <- msg
}
